package com.example.zyl.common.query.query;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.zyl.common.query.annotation.SearchField;
import com.example.zyl.common.query.enums.Operation;
import com.example.zyl.common.utils.ToolUtils;
import com.example.zyl.common.utils.constant.Constant;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

public interface BaseQueryDto<T> {

    default QueryWrapper<T> toWrapper() {
        AtomicReference<QueryWrapper<T>> wrapper = new AtomicReference<>();
        wrapper.set(new QueryWrapper<>());
        List<Field> allFields = getQueryFields(this.getClass());
        for (Field field : allFields) {
            field.setAccessible(true);
            if (field.getAnnotation(SearchField.class) != null) {
                SearchField fieldAnnotation = field.getAnnotation(SearchField.class);
                String tableFieldName = fieldAnnotation.tableField();
                if (StringUtils.isBlank(tableFieldName)) {
                    tableFieldName = field.getName();
                }
                try {
                    addOperation(wrapper.get(), tableFieldName, fieldAnnotation, field.get(this));
                    addSpecialOperation(wrapper.get());
                    addSortOperation(wrapper.get());
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return wrapper.get();
    }


    /**
     * 默认查询条件（默认查逻辑查询）
     *
     * @return QueryWrapper
     */
    default QueryWrapper<T> defaultWrapper() {
        return toWrapper();
    }


    default void addSpecialOperation(QueryWrapper<T> wrapper) {
    }

    default void addSortOperation(QueryWrapper<T> wrapper) {
    }

    default void addOperation(QueryWrapper<T> wrapper, String tableFieldName, SearchField annotation, Object value) {
        if (ToolUtils.isNotEmpty(value)) {
            boolean isArray = value.getClass().isArray();
            tableFieldName = StrUtil.toUnderlineCase(tableFieldName);
            Operation operation = annotation.value();
            switch (operation) {
                case EQ:
                    wrapper.eq(Objects.nonNull(value.toString()), tableFieldName, value);
                    break;
                case NE:
                    wrapper.ne(tableFieldName, value);
                    break;
                case GE:
                    wrapper.ge(tableFieldName, value);
                    break;
                case GT:
                    wrapper.gt(tableFieldName, value);
                    break;
                case LE:
                    wrapper.le(tableFieldName, value);
                    break;
                case LT:
                    wrapper.lt(tableFieldName, value);
                    break;
                case LIKE:
                    switch (annotation.likeType()) {
                        case LEFT:
                            wrapper.likeLeft(StringUtils.isNotBlank(value.toString()), tableFieldName, value.toString());
                            break;
                        case RIGHT:
                            wrapper.likeRight(StringUtils.isNotBlank(value.toString()), tableFieldName, value.toString());
                            break;
                        case DEFAULT:
                        default:
                            wrapper.like(StringUtils.isNotBlank(value.toString()), tableFieldName, value.toString());
                            break;
                    }
                    break;
                case NOT_LIKE:
                    switch (annotation.likeType()) {
                        case LEFT:
                            wrapper.notLikeLeft(tableFieldName, value.toString());
                            break;
                        case RIGHT:
                            wrapper.notLikeRight(tableFieldName, value.toString());
                            break;
                        case DEFAULT:
                        default:
                            wrapper.notLike(tableFieldName, value.toString());
                            break;
                    }
                    break;
                case IN:
                    if (isArray) {
                        wrapper.in(tableFieldName, (Object[]) value);
                    } else if (value instanceof Collection) {
                        wrapper.in(tableFieldName, (Collection<?>) value);
                    } else {
                        wrapper.in(tableFieldName, Collections.singletonList(value));
                    }
                    break;
                case NOTIN:
                    if (isArray) {
                        wrapper.notIn(tableFieldName, (Object[]) value);
                    } else if (value instanceof Collection) {
                        wrapper.notIn(tableFieldName, (Collection<?>) value);
                    } else {
                        wrapper.notIn(tableFieldName, Collections.singletonList(value));
                    }
                    break;
            }
        }
    }


    static void getSql(String tableFieldName, SearchField annotation, Object value, StringBuilder sql) {

        if (ToolUtils.isNotEmpty(value)) {
            sql.append(Constant.AND);
            String underlineCase = StrUtil.toUnderlineCase(tableFieldName);
            tableFieldName = Constant.OR.concat(tableFieldName);
            Operation operation = annotation.value();
            switch (operation) {
                case EQ:
                    sql.append(underlineCase).append(" = ").append(tableFieldName);
                    break;
                case NE:
                    sql.append(underlineCase).append(" <> ").append(tableFieldName);
                    break;
                case GE:
                    sql.append(underlineCase).append(" >= ").append(tableFieldName);
                    break;
                case GT:
                    sql.append(underlineCase).append(" > ").append(tableFieldName);
                    break;
                case LE:
                    sql.append(underlineCase).append(" <= ").append(tableFieldName);
                    break;
                case LT:
                    sql.append(underlineCase).append(" < ").append(tableFieldName);
                    break;
                case LIKE:
                    switch (annotation.likeType()) {
                        case LEFT:
                            sql.append(underlineCase).append(" like concat('%', ").append(tableFieldName).append(" )");
                            break;
                        case RIGHT:
                            sql.append(underlineCase).append(" like concat(").append(tableFieldName).append(", '%')");
                            break;
                        case DEFAULT:
                        default:
                            sql.append(underlineCase).append(" like concat('%', ").append(tableFieldName).append(" , '%')");
                            break;
                    }
                    break;
                case NOT_LIKE:
                    switch (annotation.likeType()) {
                        case LEFT:
                            sql.append(underlineCase).append(" not like concat('%', ").append(tableFieldName).append(" )");
                            break;
                        case RIGHT:
                            sql.append(underlineCase).append(" not like concat(").append(tableFieldName).append(", '%')");
                            break;
                        case DEFAULT:
                        default:
                            sql.append(underlineCase).append(" not like concat('%', ").append(tableFieldName).append(" , '%')");
                            break;
                    }
                    break;
                case IN:
                    sql.append(underlineCase).append(" in ( ").append(tableFieldName).append(" )");
                    break;
                case NOTIN:
                    sql.append(underlineCase).append(" not in ( ").append(tableFieldName).append(" )");
                    break;
            }
        }
    }


    static List<Field> getQueryFields(Class<?> clazz) {
        List<Field> resList = new ArrayList<>();
        while (clazz != null && clazz != BaseQueryDto.class) {
            Field[] curFields = clazz.getDeclaredFields();
            if (!ArrayUtils.isEmpty(curFields)) {
                resList.addAll(Arrays.asList(curFields));
            }
            clazz = clazz.getSuperclass();
        }
        return resList;
    }
}
